/**
 * elancom Corporation copy right 2012 - 2015  
 *   
 * $Header: https://192.168.1.224/svn/elancom/product/elancom-platform/trunk/src/main/java/com/elancom/framework/dao/Hibernate4DAOImpl.java 1094 2012-07-06 12:08:51Z lanzhouxue $
 * $Id: Hibernate4DAOImpl.java 1094 2012-07-06 12:08:51Z lanzhouxue $
 * $Author: lanzhouxue $
 * $Date: 2012-07-06 20:08:51 +0800 (周五, 06 七月 2012) $
 * $Revision: 1094 $
 */
package com.elancom.framework.dao;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.InitializingBean;

import com.elancom.framework.Constants;
import com.elancom.framework.commons.limit.LimitImpl;
import com.elancom.framework.commons.util.Assert;
import com.elancom.framework.commons.util.IterEach;
import com.elancom.framework.commons.util.JoyfulUtils;
import com.elancom.framework.commons.util.MapUtils;
import com.elancom.framework.commons.util.StringUtils;
import com.elancom.framework.dao.expression.Condition;
import com.elancom.framework.dao.expression.Equal;
import com.elancom.framework.entity.AbstractEntity;
import com.elancom.framework.entity.DeletedSupport;
import com.elancom.framework.entity.Entity;

/**
 * @author elancom
 * @date Apr 15, 2012
 */
@SuppressWarnings("unchecked")
public class Hibernate4DAOImpl<M extends AbstractEntity> implements AbstractDAO<M>, InitializingBean {
	protected Class<M> entityClass = null;

	protected String entityName = null;

	@Resource
	private HibernateSessionHolder hibernateSessionHolder;

	private boolean vmrSupport = false;

	@Override
	public void afterPropertiesSet() throws Exception {
		if (entityClass == null) {
			entityClass = doGetEntityClass();
			Assert.notNull(entityClass);
		}

		entityName = entityClass.getName();

		Class<?>[] interClasses = entityClass.getInterfaces();
		for (Class<?> c : interClasses) {
			if (c == DeletedSupport.class) {
				vmrSupport = true;
			}
		}
	}

	@Override
	public boolean contains(M entity) {
		return getSession().contains(entity);
	}

	@Override
	public int count() {
		return count((String) null);
	}

	@Override
	public int count(Condition condition) {
		return count(condition.toHQLString());
	}

	@Override
	public int count(Map<String, Object> conditionMap) {
		String conditionStr = null;
		if (conditionMap.size() == 1) {
			Entry<String, Object> entry = conditionMap.entrySet().iterator().next();
			conditionStr = entry.getKey() + "= :" + entry.getKey();
		} else {
			StringBuilder sb = new StringBuilder();
			String key = null;
			for (Entry<String, Object> entry : conditionMap.entrySet()) {
				key = entry.getKey();
				sb.append(key).append("= :").append(entry.getKey());
			}
			conditionStr = sb.toString();
		}

		return count(conditionStr, conditionMap);
	}

	@Override
	public int count(String conditionStr) {
		return count(conditionStr, null);
	}

	@Override
	public int count(String condition, Map<String, Object> namedParameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("select count(id) from ");
		sb.append(entityName);

		if (condition != null) {
			sb.append(" where ").append(condition);
		}

		if (vmrSupport) {
			if (condition == null) {
				sb.append(" where");
			} else {
				sb.append(" and");
			}
			// if (sb.indexOf("deleted") == -1) { // ELANCOM-FIXME:去掉检查,原因是个BUG
			sb.append(" deleted = 0");
			// }
		}

		Query query = getSession().createQuery(sb.toString());
		setQueryParameters(query, namedParameters);
		Long count = (Long) query.uniqueResult();
		return count.intValue();
	}

	protected Class<M> doGetEntityClass() {
		return this.entityClass = (Class<M>) ((ParameterizedType) getClass()//
				.getGenericSuperclass())//
				.getActualTypeArguments()[0];
	}

	@Override
	public void evictCache(M entity) {
		getSession().evict(entity);
	}

	@Override
	public M getById(String id) {
		if (id == null || id.trim().length() == 0) {
			return null;
		}
		return (M) getSession().get(entityClass, id);
	}

	@Override
	public M getById(String id, String... fetchAssociation) {
		if (id == null || id.trim().length() == 0) {
			return null;
		}

		StringBuffer sb = new StringBuffer();
		sb.append("select M from ").append(entityName);

		if (fetchAssociation != null && fetchAssociation.length > 0) {
			for (String assEntity : fetchAssociation) {
				sb.append(" left join fetch M.").append(assEntity);
			}
		}

		sb.append(" where M.id = :id");

		return (M) (getSession().createQuery(sb.toString()).setParameter("id", id).uniqueResult());
	}

	@Override
	public List<M> getByIds(String ids) {
		String[] idsArr = null;
		if (StringUtils.isNotBlank(ids)) {
			idsArr = ids.split(",");
		}
		return getByIds(idsArr);
	}

	@Override
	public List<M> getByIds(String[] ids) {
		if (ids == null || ids.length == 0) {
			return new ArrayList<M>(0);
		}

		String inStr = StringUtils.wrap(ids, "'", ",");

		StringBuffer sb = new StringBuffer();
		sb.append("from ").append(entityName).append(" where id in (").append(inStr).append(")");

		return getSession().createQuery(sb.toString()).list();
	}

	public HibernateSessionHolder getHibernateSessionHolder() {
		return hibernateSessionHolder;
	}

	@Override
	public List<M> selectList() {
		return selectList((Map<String, Object>) null);
	}

	public Session getSession() {
		Session session = hibernateSessionHolder.getSession();
		// DEBUG SESSION
		return session;
	}

	@Override
	public void insert(Collection<M> entities) {
		if (entities == null || entities.isEmpty()) {
			return;
		}

		for (M entity : entities) {
			insert(entity);
		}
	}

	@Override
	public void insert(M entity) {
		if (entity instanceof Entity) {
			getSession().save(entity);
		} else {
			throw new RuntimeException("不支持该实体的持久化操作");
		}
	}

	@Override
	public void insert(Map<Object, M> entityMap) {
		JoyfulUtils.iterEach(entityMap, new IterEach<Object, M>() {
			@Override
			public boolean each(Object key, M value) {
				if (value != null) {
					insert(value);
				}
				return true;
			}
		});
	}

	@Override
	public boolean isExist(String id) {
		return id == null || id.isEmpty() ? false : count(MapUtils.createMap("id", (Object) id)) != 0;
	}

	@Override
	public M loadById(String id) {
		return (M) getSession().load(entityClass, id);
	}

	@Override
	public M mergr(M entity) {
		return (M) getSession().merge(entity);
	}

	@Override
	public void refresh(M entity) {
		getSession().refresh(entity);
	}

	@Override
	public void remove(Collection<M> entities) {
		if (entities == null || entities.isEmpty()) {
			return;
		}

		for (M entity : entities) {
			remove(entity);
		}
	}

	@Override
	public void remove(M entity) {
		if (entity != null && entity.getId() != null) {
			if (vmrSupport) {
				((DeletedSupport) entity).setDeleted(Constants.INT_STATE_TRUE);
				update(entity);
			} else {
				getSession().delete(entity);
			}
		}
	}

	@Override
	public void remove(String id) {
		if (id != null && id.trim().length() != 0) {
			remove(getById(id));
		}
	}

	@Override
	public void removes(String ids) {
		if (StringUtils.isBlank(ids)) {
			return;
		}

		String inIds = StringUtils.wrap(ids, "'", ",");
		if (vmrSupport) {
			StringBuffer sb = new StringBuffer();
			sb.append("update ").append(entityName);
			sb.append(" set deleted = ").append(Constants.INT_STATE_TRUE);
			sb.append(" where id in(").append(inIds).append(")");
			getSession().createQuery(sb.toString()).executeUpdate();
		} else {
			// check check check....
			StringBuffer sb = new StringBuffer();
			sb.append("delete ").append(entityName);
			sb.append(" where id in(").append(ids).append(")");
			getSession().createQuery(sb.toString()).executeUpdate();
		}
	}

	public void setHibernateSessionHolder(HibernateSessionHolder hibernateSessionHolder) {
		this.hibernateSessionHolder = hibernateSessionHolder;
	}

	private void setQueryParameters(Query query, Map<String, Object> namedParameters) {
		if (namedParameters != null && !namedParameters.isEmpty()) {
			for (Entry<String, Object> entry : namedParameters.entrySet()) {
				query.setParameter(entry.getKey(), entry.getValue());
			}
		}
	}

	@Override
	public void update(Collection<M> entities) {
		if (entities == null || entities.isEmpty()) {
			return;
		}

		for (M entity : entities) {
			update(entity);
		}
	}

	@Override
	public void update(M entity) {
		getSession().update(entity);
	}

	@Override
	public int update(UpdateCompiled update) {
		Query query = getSession().createQuery(update.getUpdateString());
		setQueryParameters(query, update.getParameterMap());
		return query.executeUpdate();
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#count(com.elancom.framework.dao.SelectSupport)
	 */
	@Override
	public int count(SelectCompiled select) {
		String countString = select.getCountString();
		if (countString == null || countString.trim().length() == 0) {
			return 0;
		}

		Query query = getSession().createQuery(countString);
		setQueryParameters(query, select.getParameterMap());
		return ((Long) query.uniqueResult()).intValue();
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#selectList(com.elancom.framework.dao.expression.Condition)
	 */
	@Override
	public List<M> selectList(Condition condition) {
		return selectList(condition, -1, -1).getResult();
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#selectList(com.elancom.framework.dao.expression.Condition, int, int)
	 */
	@Override
	public Result selectList(Condition condition, int curPage, int pageSize) {
		Map<String, Object> conditionMap = new HashMap<String, Object>(1);
		conditionMap.put("___", condition);
		return selectList(conditionMap, curPage, pageSize);
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#selectList(int, int)
	 */
	@Override
	public Result selectList(int curPage, int pageSize) {
		return selectList((Map<String, Object>) null, curPage, pageSize);
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#selectList(java.util.Map)
	 */
	@Override
	public List<M> selectList(Map<String, Object> condition) {
		return selectList(condition, -1, -1).getResult();
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#selectList(java.util.Map, int, int)
	 */
	@Override
	public Result selectList(Map<String, Object> conditionMap, int curPage, int pageSize) {
		Result result = new Result();
		Criteria c = getSession().createCriteria(entityClass);
		addCriterion(conditionMap, c);
		if (vmrSupport) {
			c.add(Restrictions.eq("deleted", 0));
		}
		result.setResult(c.list());

		LimitImpl limit = new LimitImpl();
		if (curPage > -1 && !result.isEmpty()) {
			Criteria cc = getSession().createCriteria(entityClass);
			addCriterion(conditionMap, cc);
			if (vmrSupport) {
				cc.add(Restrictions.eq("deleted", 0));
			}
			cc.setProjection(Projections.count("id"));
			Integer count = (Integer) cc.uniqueResult();
			limit.setCurPage(curPage);
			limit.setTotalResults(count);
		}

		result.setLimit(limit);
		return result;
	}

	/**
	 * @param condition
	 * @param c
	 */
	private void addCriterion(Map<String, Object> condition, Criteria c) {
		if (condition == null || condition.isEmpty()) {
			return;
		}

		String key = null;
		Object value = null;
		for (Entry<String, Object> entry : condition.entrySet()) {
			key = entry.getKey();
			value = entry.getValue();
			if (value instanceof Condition) {
				c.add(((Condition) value).toCriterion());
			} else {
				c.add(new Equal(key, value).toCriterion());
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.GenericDAO#select(com.elancom.framework.dao.SelectSupport)
	 */
	@Override
	public Result select(SelectCompiled select) {
		Result result = new Result();

		String selectString = select.getSelectString();
		Query query = getSession().createQuery(selectString);
		setQueryParameters(query, select.getParameterMap());
		List<?> list = query.list();
		result.setResult(list);

		LimitImpl limit = new LimitImpl();
		if (select.isLimit()) {
			limit.setCurPage(select.getCurPage());
			limit.setPageSize(select.getMaxResults());

			if (list.isEmpty()) {
				limit.setTotalResults(0);
			} else {
				limit.setTotalResults(count(select));
			}
			result.setLimit(limit);
		}

		result.setResult(list);
		return result;
	}
}
